package zzh.fanshe1;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestDriver {
    /**
     * Class反射工具类获取的途径
     * 结论：不管通过什么途径，一个类只有唯一的一个Class描述类；
     * Class是一个单例模式；
     * 反射编程，一般都是从获取一个类的Class类开始；
     */

    @Test
    public  void  test01(){

        //方式一  类名.Class获取
        Class c = Student.class;
        //方式二  通过实例对象.getClass方法获取
        Student student = new Student();
        Class c1 = student.getClass();
        //方式三  使用Class的静态方法forName获取一个给定的完整的包路径名类的Class
        Class c2 = null;
        try {
             c2 = Class.forName("zzh.fanshe1.Student"); // 完整包路径名
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println("c==c1"+"  "+(c==c1));
        System.out.println("c1==c2"+"  "+(c1==c2));
        System.out.println("c==c2"+"  "+(c==c2));
    }

    /**
     * 通过反射机制，使用Class获取被描述类的实例对象
     */
    @Test
    public  void  test02(){
        //方式一  类名.Class获取
        Class<Student> c = Student.class;
        //使用Class工具类的newInstance方法获取被描述类的实例对象  等价于new操作
        try {
            Student student =  c.newInstance();
            Student student1 = c.newInstance();

            System.out.println("student == student1"+"  "+(student==student1));
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
    /**
     * 通过反射机制，使用Constructor获取被描述类的实例对象
     *
     * Java中获取类的实例对象的方式：
     * 1、	new
     * 2、	对象流
     * 3、	反射（通过Class.newInstance   constructor.newInstance）
     * 4、	深克隆（深复制）
     */
    @Test
    public  void  test03(){
        // 方式二 通过实例对象.getClass方法获取
        Student student = new Student();
        Class c = student.getClass();

        try {
            Constructor<Student> constructor = c.getDeclaredConstructor();
            Constructor<Student> constructor1 = c.getDeclaredConstructor();
            Constructor<Student> constructor2 = c.getDeclaredConstructor(String.class);
            Constructor<Student> constructor3 = c.getDeclaredConstructor(String.class,int.class,String.class,String.class);
            // 获取Constructor对象不是单例模式,二者不相等
            System.out.println("constructor == constructor2:" + (constructor == constructor2));
            // 使用Constructor获取类的实力对象
            Student student1 = constructor.newInstance();
            //使用newInstance方法时，需要和获取时给定的参数列表保持一致
            Student student4 =constructor3.newInstance("zs",20,"10086","男");
            System.out.println("student4:" + student4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 反射操作
     * 可以借助于第三方的工具： BeanUtils
     */
    @Test
    public  void  test04(){
        // 方式二 通过实例对象.getClass方法获取
        Student student = new Student();
        Student student1 = new Student("张三", 20, "10086", "男");
        Class c = student.getClass();
        //获取属性的描述类Field
        try {
            System.out.println("反射前："+student1);
            //获取属性的描述类Field
            Field fieldStuName = c.getDeclaredField("stuName");
            // 设置反射机制可以操作私有类型
            fieldStuName.setAccessible(true);
            // 采用反射机制，动态操作Field对象
            // 第一个参数是反射操作时需要附加的实例对象；
            // 第二个参数是该属性的设置的值
            fieldStuName.set(student1, "李四");

            System.out.println("反射之后：" + student1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * Method工具类
     */
    @Test
    public  void  test05(){
        // 2 通过实例对象.getClass方法获取
        Student student = new Student("lisa");
        Student student1 = new Student("张三", 20, "10086", "男");
        Class c = student.getClass();
        try {
            // 获取Method对象
            // 如果有重载的话，通过参数列表来区分
            Method method = c.getDeclaredMethod("doSuding");
            // 第一个参数是 方法名；
            // 第二个参数是 从在方法的参数列表；参数列表需要使用 Class 来表示
            Method method1 = c.getDeclaredMethod("doSuding", String.class);
            // 使用反射机制的 invoke 途径，执行该方法对象
            method.invoke(student);
            method1.invoke(student1,"library");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
